﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using MessageHandler;
using Msg = MessageHandler.Message;

namespace Chatclient
{
	public partial class FormMessengerClient : Form
	{
		Controller controller;
		UserInfo userInfo;
		ErrorHandler errorHandler = new ErrorHandler();

		private bool loggedOut = false;

		public FormMessengerClient(Controller controller)
		{
			InitializeComponent();

			this.controller = controller;
			this.controller = controller;
			controller.MessageReceived += new Controller.MessageReceivedEventHandler(controller_MessageReceived);
			controller.ErrorOccured += new Controller.ErrorOccuredEventHandler(controller_ErrorOccured);
			this.userInfo = controller.UserInfo;
			this.Text = userInfo.username + " - Messenger";
			controller.UserListUpdate();
		}

		#region Incoming Message- Handling

		private delegate void HandleMessageCallback(Msg message);

		private void HandleMessage(Msg message)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new HandleMessageCallback(HandleMessage), message);
			}
			else
			{
				switch (message.messageType)
				{
					case MessageType.PRIVATE:
						HandlePrivateMessage(message);
						break;
					case MessageType.USERLISTUPDATE:
						HandleUserListUpdate(message);
						break;
					case MessageType.CONTACTADD:
						HandleContactAddMessage(message);
						break;
					case MessageType.USERLOGIN:
						HandleUserLogin(message);
						break;
					case MessageType.USERLOGOUT:
						HandleUserLogout(message);
						break;
					case MessageType.STARTFILETRANSFER:
						HandleStartFileTransfer(message);
						break;
					case MessageType.FILETRANSFER:
						HandleFileTransfer(message);
						break;
					case MessageType.ERROR:
						HandleError(message);
						break;
					default:
						break;
				}
			}
		}

		private void HandleUserLogout(Msg message)
		{
			Application.DoEvents();
			DoLogout();
		}

		private void DoLogout()
		{
			loggedOut = true;
			new System.Threading.Thread(() => Application.Run(new FormLogin())).Start();
			CloseForm();
		}

		private delegate void CloseFormCallback();
		private void CloseForm()
		{
			if (this.InvokeRequired)
			{
				CloseFormCallback cfc = new CloseFormCallback(CloseForm);
				this.Invoke(cfc);
			}
			else
			{
				this.Close();
			}
		}

		private void HandleUserLogin(Msg message)
		{
			UserLoginMessageType type = (UserLoginMessageType)message.Item;
			this.userInfo = type.newUser;
			UpdateStatusLabel("Eingeloggt als: " + type.newUser.ToString());
		}

		private void HandleUserListUpdate(Msg message)
		{
			UserListUpdateMessageType type = (UserListUpdateMessageType)message.Item;
			UpdateUserList(type.currentUserList);
		}

		private void HandleContactAddMessage(Msg message)
		{
			ContactAddMessageType type = (ContactAddMessageType)message.Item;
			if (type.toUser.Equals(this.userInfo))
			{
				var result = MessageBox.Show("Möchten Sie " + type.fromUser.username + " zu Ihren Kontakten hinzufügen?", "Kontakt hinzufügen", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
				if (result == System.Windows.Forms.DialogResult.Yes)
				{
					controller.ConfirmContact(type.fromUser);
				}
				else
				{
					UserInfo[] users = { type.fromUser };
					controller.SendErrorMessage(users, "Die Kontaktanfrage wurde nicht angenommen.");
				}
			}
		}

		private void HandleError(Msg message)
		{
			ErrorMessageType type = (ErrorMessageType)message.Item;
			errorHandler.ShowError(new ChatException(message.text, type.from.username + " teilt mit"));
		}

		private void HandlePrivateMessage(Msg message)
		{
			PrivateMessageType type = (PrivateMessageType)message.Item;
			UserInfo[] users = GetUserArray(type.from, type.to);
			HandleWritePrivateChat(message, type.from, users);
		}

		private void HandleStartFileTransfer(Msg message)
		{
			StartFileTransferMessageType type = (StartFileTransferMessageType)message.Item;
			UserInfo[] users = GetUserArray(type.from, type.to);
			HandleWritePrivateChat(message, type.from, users);
		}

		private void HandleFileTransfer(Msg message)
		{
			FileTransferMessageType type = (FileTransferMessageType)message.Item;
			UserInfo[] users = GetUserArray(type.from, type.to);
			HandleWritePrivateChat(message, type.from, users);
		}

		private UserInfo[] GetUserArray(UserInfo userInfo, UserInfo[] userInfos)
		{
			UserInfo[] users = new UserInfo[userInfos.Length + 1];
			Array.Copy(userInfos, users, userInfos.Length);
			users[users.Length - 1] = userInfo;
			return users;
		}

		private void HandleWritePrivateChat(Msg message, UserInfo thisUser, UserInfo[] userInfos)
		{
			FormPrivateChat fpc = (FormPrivateChat)Application.OpenForms[GetPrivateChatName(thisUser, userInfos)];
			if (fpc != null)
			{
				WritePrivateMessage(message, fpc);
			}
			else
			{
				OpenPrivateChat(userInfos);
				fpc = (FormPrivateChat)Application.OpenForms[GetPrivateChatName(this.userInfo, userInfos)];
				WritePrivateMessage(message, fpc);
			}
		}

		private void WritePrivateMessage(Msg message, FormPrivateChat privateChat)
		{
			switch (message.messageType)
			{
				case MessageType.PRIVATE:
					privateChat.WriteMessage(message);
					if (!((PrivateMessageType)message.Item).from.Equals(this.userInfo))
						PlaySound();
					break;
				case MessageType.FILETRANSFER:
					PlaySound();
					privateChat.WriteFile(message);
					break;
				case MessageType.STARTFILETRANSFER:
					PlaySound();
					privateChat.HandleStartFileTransfer(message);
					break;
				default:
					break;
			}
		}

		private void PlaySound()
		{
			new System.Media.SoundPlayer("message.wav").Play();
		}


		#endregion

		#region Events

		void controller_ErrorOccured(object sender, ErrorOccuredEventArgs e)
		{
			errorHandler.ShowError(e.Error);
		}

		void controller_MessageReceived(object sender, MessageReceivedEventArgs e)
		{
			HandleMessage(e.Message);
		}

		private void btnLogout_Click(object sender, EventArgs e)
		{
			ClosePrivateForms();
			Logout();
		}

		private void btnPrivate_Click(object sender, EventArgs e)
		{
			StartPrivateChat();
		}

		private void lstBxUsers_MouseDoubleClick(object sender, MouseEventArgs e)
		{
			StartPrivateChat();
		}

		private void kontakteHinzufügenToolStripMenuItem_Click(object sender, EventArgs e)
		{
			new FormContacts(controller).ShowDialog();
		}

		private void meinProfilToolStripMenuItem_Click(object sender, EventArgs e)
		{
			FormProfile fp = new FormProfile(controller, userInfo, true);
			if (fp.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				controller.UpdateProfile(fp.UserInfo);
			}
		}

		private void chatStartenToolStripMenuItem1_Click(object sender, EventArgs e)
		{
			StartPrivateChat();
		}

		private void profilAnzeigenToolStripMenuItem_Click(object sender, EventArgs e)
		{
			UserInfo ui = (UserInfo)lstBxUsers.SelectedItems[0];
			new FormProfile(controller, ui, false).ShowDialog();
		}

		private void lstBxUsers_SelectedValueChanged(object sender, EventArgs e)
		{
			if (lstBxUsers.SelectedItems.Contains(this.userInfo))
			{
				lstBxUsers.SelectedItems.Remove(this.userInfo);
			}
			if (lstBxUsers.SelectedItems.Count > 0)
			{
				chatStartenToolStripMenuItem.Enabled = true;
				chatStartenToolStripMenuItem1.Enabled = true;
				profilAnzeigenToolStripMenuItem.Enabled = true;
			}
			else
			{
				chatStartenToolStripMenuItem.Enabled = false;
				chatStartenToolStripMenuItem1.Enabled = false;
				profilAnzeigenToolStripMenuItem.Enabled = false;
			}
		}

		#endregion

		#region Work

		#region Public

		private void UpdateUserList(UserInfo[] userInfos)
		{
			if (userInfos == null)
			{
				lstBxUsers.Items.Clear();
			}
			else
			{

				List<object> uis = new List<object>();
				foreach (object obj in lstBxUsers.SelectedItems)
				{
					uis.Add(obj);
				}
				lstBxUsers.Items.Clear();
				lstBxUsers.Items.AddRange(userInfos);

				foreach (object obj in uis)
				{
					if (lstBxUsers.Items.Contains(obj))
					{
						lstBxUsers.SelectedItems.Add(obj);
					}
				}
			}
		}

		private void Logout()
		{
			try
			{
				controller.Logout(this.userInfo);
			}
			catch (ChatException)
			{

			}
		}

		private void ClosePrivateForms()
		{
			int count = Application.OpenForms.Count;
			int idx = 0;
			for (int i = 0; i < count; ++i)
			{
				if (Application.OpenForms[idx].Name != this.Name)
				{
					Application.OpenForms[idx].Close();
				}
				else
				{
					++idx;
				}
			}
		}

		internal void CloseConnection()
		{
			controller.Close();
		}

		private delegate void UpdateStatusLabelCallback(string text);
		[STAThread]
		private void UpdateStatusLabel(string text)
		{
			if (this.InvokeRequired)
			{
				UpdateStatusLabelCallback cb = new UpdateStatusLabelCallback(UpdateStatusLabel);
				this.Invoke(cb, text);
			}
			else
			{
				this.tlSLblStatus.Text = text;
			}
		}

		#endregion

		#region Private

		private void StartPrivateChat()
		{
			if (lstBxUsers.SelectedItems.Count > 0)
			{
				UserInfo[] userInfos = new UserInfo[lstBxUsers.SelectedItems.Count + 1];
				lstBxUsers.SelectedItems.CopyTo(userInfos, 0);
				userInfos[userInfos.Length - 1] = this.userInfo;
				OpenPrivateChat(userInfos);
			}
		}

		private void OpenPrivateChat(UserInfo[] userInfos)
		{
			string formName = GetPrivateChatName(this.userInfo, userInfos);
			if (Application.OpenForms[formName] == null)
			{
				FormPrivateChat fpc = new FormPrivateChat(controller, userInfos, this.userInfo);
				fpc.Name = formName;
				fpc.Show();
			}
			else
			{
				Application.OpenForms[formName].WindowState = FormWindowState.Normal;
				Application.OpenForms[formName].BringToFront();
			}
		}

		private string GetPrivateChatName(UserInfo thisUser, params UserInfo[] userInfos)
		{
			PrivateChatMembers tmpMembers = new PrivateChatMembers(userInfos, thisUser);
			return tmpMembers.GetHashCode().ToString();
		}

		#endregion

		#endregion

		private void FormChatClient_FormClosing(object sender, FormClosingEventArgs e)
		{
			CloseConnections();
		}

		private void CloseConnections()
		{
			CloseConnection();
			if (!loggedOut)
			{
				int count = Application.OpenForms.Count;
				int idx = 0;
				for (int i = 0; i < count; ++i)
				{
					if (Application.OpenForms[idx].Name != this.Name)
					{
						Application.OpenForms[idx].Close();
					}
					else
					{
						++idx;
					}
				}
			}
			Application.DoEvents();
		}

		private void FormMessengerClient_FormClosed(object sender, FormClosedEventArgs e)
		{
			if (Application.OpenForms.Count == 0)
			{
				Application.Exit();
			}
		}
	}
}
